package logic

import (
	"context"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"errors"
	"fmt"
	"log"
	"regexp"
	"strings"
	"sync/atomic"
	"time"

	"github.com/coder/websocket"
	"github.com/coder/websocket/wsjson"
	"github.com/spf13/cast"
	"github.com/spf13/viper"
)

// 系统用户
var System = &User{}

var globalUID int32 = 0

type User struct {
	UID            int           `json:"uid"`
	NickName       string        `json:"nickname"`
	EnterAt        time.Time     `json:"enter_at"`
	Addr           string        `json:"addr"`
	Token          string        `json:"token"`
	messageChannel chan *Message `json:"-"`

	Conn  *websocket.Conn
	IsNew bool `json:"is_new"`
}

func NewUser(conn *websocket.Conn, nickname, token, addr string) *User {
	user := &User{
		NickName:       nickname,
		Addr:           addr,
		Token:          token,
		Conn:           conn,
		messageChannel: make(chan *Message),
	}

	//不是第一次加入
	if user.Token != "" {
		uid, err := parseTakenAndValidate(token, nickname)
		if err == nil {
			user.UID = uid
		}
	}

	if user.UID == 0 {
		user.UID = int(atomic.AddInt32(&globalUID, 1))
		user.Token = getToken(user.UID, user.NickName)
		user.IsNew = true
	}
	return user
}

func getToken(uid int, nickname string) string {
	secret := viper.GetString("token-secret")
	message := fmt.Sprintf("%s%s%d", nickname, secret, uid)

	messageMAC := macSha256([]byte(message), []byte(secret))

	return fmt.Sprintf("%suid%d", base64.StdEncoding.EncodeToString(messageMAC), uid)
}

func macSha256(message, secret []byte) []byte {
	mac := hmac.New(sha256.New, secret)
	mac.Write(message)
	return mac.Sum(nil)
}

func parseTakenAndValidate(token, nickname string) (int, error) {
	pos := strings.LastIndex(token, "uid")
	if pos < 0 {
		return 0, errors.New("token is illegal")
	}

	messageMAC, err := base64.StdEncoding.DecodeString(token[:pos])
	if err != nil {
		return 0, err
	}

	uid := cast.ToInt(token[pos+3:])
	secret := viper.GetString("token-secret")
	message := fmt.Sprintf("%s%s%d", nickname, secret, uid)

	ok := validateMAC([]byte(message), messageMAC, []byte(secret))
	if ok {
		return uid, nil
	}
	return 0, errors.New("token is illegal")
}

func validateMAC(message, messageMAC, secret []byte) bool {
	mac := hmac.New(sha256.New, secret)
	mac.Write(message)
	expectedMAC := mac.Sum(nil)
	//重新生成看是否相等
	return hmac.Equal(expectedMAC, messageMAC)
}

func (u *User) SendMessage(ctx context.Context) {
	for msg := range u.messageChannel {
		err := wsjson.Write(ctx, u.Conn, msg)
		if err != nil {
			log.Println("SendMessage wsjson.Write err:", err)
		}
	}
}

func (u *User) ReceiveMessage(ctx context.Context) error {
	var (
		receiveMsg map[string]string
		err        error
	)
	for {
		err = wsjson.Read(ctx, u.Conn, &receiveMsg)
		if err != nil {
			//判断连接是否关闭，若正常关闭，则不认为是错误
			var closeErr websocket.CloseError
			if errors.As(err, &closeErr) {
				return nil
			}
			return err
		}
		//把内容发送到聊天室
		sendMsg := NewMessage(u, receiveMsg["content"], receiveMsg["send_time"])
		sendMsg.Content = FilerSensitive(sendMsg.Content)
		//将传入的正则表达式字符串编译成一个 *regexp.Regexp 对象。
		//作用：匹配以 @ 开头，后面跟着长度在 2 到 20 个字符之间，
		// 且这些字符不能是空白字符和 @ 的字符串
		reg := regexp.MustCompile(`@[^\s@]{2,20}`)
		//如@123@234 sendMtsg.Ats中包含[@123,@234]
		sendMsg.Ats = reg.FindAllString(sendMsg.Content, -1)
		fmt.Println(sendMsg.Ats)
		BroadCaster.Broadcast(sendMsg)
	}
}

func (u *User) CloseMessageChannel() {
	close(u.messageChannel)
}

func (u *User) AddUserMessage(msg *Message) {
	u.messageChannel <- msg
}
